package xlog

import (
	"encoding/json"
	"fmt"
	"github.com/lestrrat-go/file-rotatelogs"
	"github.com/pkg/errors"
	"github.com/rifflock/lfshook"
	"github.com/sirupsen/logrus"
	"os"
	"path"
	"runtime"
	"strings"
	"time"
)

var level = logrus.TraceLevel

var logger *logrus.Logger

func init() {
	logger = logrus.New()
	logger.Level = level
	logger.WithTime(time.Now())
	logger.SetFormatter(&logrus.TextFormatter{
		TimestampFormat:  "2006-01-02 15:04:05.000",
		FullTimestamp:    true,
		PadLevelText:     false,
		DisableColors:    false,
		DisableQuote:     true,
		CallerPrettyfier: callerPrettyfier,
	})
	ConfigLocalFileSystemLogger("logfile", "log", time.Second*30, time.Second*10)

	logger.Out = os.Stdout
}
func logCallerfilePath(loggingFilePath string) string {
	idx := strings.LastIndexByte(loggingFilePath, '/')
	if idx == -1 {
		return loggingFilePath
	}
	idx = strings.LastIndexByte(loggingFilePath[:idx], '/')
	if idx == -1 {
		return loggingFilePath
	}
	return loggingFilePath[idx+1:]
}
func callerPrettyfier(f *runtime.Frame) (string, string) {
	filename := logCallerfilePath(f.File)
	return "", fmt.Sprintf("%s:%d", filename, f.Line)
}

// 日志分隔
func ConfigLocalFileSystemLogger(logPath string, logFileName string, maxAge time.Duration, rotationTime time.Duration) {
	baseLogPaht := path.Join(logPath, logFileName)
	writer, err := rotatelogs.New(
		baseLogPaht+".%Y-%m-%d",              // 分割后的文件名称
		rotatelogs.WithLinkName(baseLogPaht), // 生成软链，指向最新日志文件
		//rotatelogs.WithMaxAge(maxAge),             // 设置最大保存时间(7天)
		rotatelogs.WithRotationCount(7),           // 最多存365个文件
		rotatelogs.WithRotationTime(rotationTime), // 设置日志切割时间间隔(1天)
	)
	if err != nil {
		logrus.Errorf("config local file system logger error. %+v", errors.WithStack(err))
	}
	lfHook := lfshook.NewHook(lfshook.WriterMap{
		logrus.DebugLevel: writer, // 为不同级别设置不同的输出目的
		logrus.InfoLevel:  writer,
		logrus.WarnLevel:  writer,
		logrus.ErrorLevel: writer,
		logrus.FatalLevel: writer,
		logrus.PanicLevel: writer,
	}, &logrus.TextFormatter{})
	logger.SetReportCaller(true)
	logger.AddHook(lfHook)
	NewCallerHook(logger)

}

// Log makes use of github.com/go-log/log.Log
func Log(v ...interface{}) {

	logger.Log(logrus.InfoLevel, v...)
}

// Logf makes use of github.com/go-log/log.Logf
func Logf(format string, v ...interface{}) {
	logger.Logf(logrus.InfoLevel, format, v...)

}

// WithLevel logs with the level specified
func WithLevel(l logrus.Level, v ...interface{}) {
	if l > level {
		return
	}
	logger.Log(l, v...)

}

// WithLevelf logs with the level specified
func WithLevelf(l logrus.Level, format string, v ...interface{}) {
	logger.Logf(l, format, v...)

}

// Trace provides trace level logging
func Trace(v ...interface{}) {
	WithLevel(logrus.TraceLevel, v...)
}

// Tracef provides trace level logging
func Tracef(format string, v ...interface{}) {
	WithLevelf(logrus.TraceLevel, format, v...)
}

// Debug provides debug level logging
func Debug(v ...interface{}) {
	WithLevel(logrus.DebugLevel, v...)
}

// Debugf provides debug level logging
func Debugf(format string, v ...interface{}) {
	WithLevelf(logrus.DebugLevel, format, v...)
}

// Warn provides warn level logging
func Warn(v ...interface{}) {
	WithLevel(logrus.WarnLevel, v...)
}

// Warnf provides warn level logging
func Warnf(format string, v ...interface{}) {
	WithLevelf(logrus.WarnLevel, format, v...)
}

// Info provides info level logging
func Info(v ...interface{}) {
	WithLevel(logrus.InfoLevel, v...)
}

// Infof provides info level logging
func Infof(format string, v ...interface{}) {
	WithLevelf(logrus.InfoLevel, format, v...)
}

// Error provides warn level logging
func Error(v ...interface{}) {
	WithLevel(logrus.ErrorLevel, v...)
}

// Errorf provides warn level logging
func Errorf(format string, v ...interface{}) {
	WithLevelf(logrus.ErrorLevel, format, v...)
}

// Fatal logs with Log and then exits with os.Exit(1)
func Fatal(v ...interface{}) {
	WithLevel(logrus.FatalLevel, v...)
	os.Exit(1)
}

// Fatalf logs with Logf and then exits with os.Exit(1)
func Fatalf(format string, v ...interface{}) {
	WithLevelf(logrus.FatalLevel, format, v...)
	os.Exit(1)
}

// Pretty provides pretty trace level logging
func Pretty(v ...interface{}) {
	b, _ := json.MarshalIndent(v, "", "  ")
	WithLevel(logrus.TraceLevel, "\n", string(b))
}

// Prettyf provides pretty trace level logging
func Prettyf(format string, v ...interface{}) {
	b, _ := json.MarshalIndent(v, "", "  ")
	WithLevelf(logrus.TraceLevel, format, string(b))
}

// PrettyLog provides pretty logging with level specified
func PrettyLog(l logrus.Level, v ...interface{}) {
	b, _ := json.MarshalIndent(v, "", "  ")
	WithLevel(l, "\n", string(b))
}

// PrettyLogf provides pretty logging with level specified
func PrettyLogf(l logrus.Level, format string, v ...interface{}) {
	b, _ := json.MarshalIndent(v, "", "  ")
	WithLevelf(l, format, string(b))
}

// GetLogger returns the local logger
func GetLogger() *logrus.Logger {
	return logger
}

// SetLevel sets the log level
func SetLevel(l string) {
	switch l {
	case "trace":
		level = logrus.TraceLevel
	case "debug":
		level = logrus.DebugLevel
	case "warn":
		level = logrus.WarnLevel
	case "info":
		level = logrus.InfoLevel
	case "error":
		level = logrus.ErrorLevel
	case "fatal":
		level = logrus.FatalLevel
	default:
		level = logrus.TraceLevel
	}
	logger.SetLevel(level)
}

// GetLevel returns the current level
func GetLevel() logrus.Level {
	return level
}
